#include "stdafx.h"
#include "_GraphArea.h"

/**
* @brief Constructor
*
* @param Layout - Graph Layout with dimensions for drawing
*/
_GraphArea::_GraphArea(_GraphLayout& Layout):
    m_Layout(Layout),
    m_SelectionActive(false),
    m_MouseTrackingEnabled(false)
{
}

/**
* @brief Default destructor
*/
_GraphArea::~_GraphArea(void)
{
}

void _GraphArea::draw(_u32 BorderSize, _u32 YAxisWidth, _u32 XAxisHeight, _u32 TitleHeight, double XMin, double XMax,
                      double YMin, double YMax, map<int, _GraphData>& GraphData, CPoint CursorPosition)
{
    m_XMin = XMin;
    m_XMax = XMax;
    m_YMin = YMin;
    m_YMax = YMax;
    m_XRange = m_XMax - m_XMin;
    m_YRange = m_YMax - m_YMin;
    glViewport(m_Layout.m_YAxisWidth + m_Layout.m_BorderSize,
               m_Layout.m_XAxisHeight + m_Layout.m_BorderSize,
               m_Layout.m_ControlRect.Width() - m_Layout.m_YAxisWidth - 2 * m_Layout.m_BorderSize,
               m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - m_Layout.m_TitleHeight - 2 * m_Layout.m_BorderSize);
    CRect Rect = CRect(YAxisWidth + m_Layout.m_BorderSize,
                       m_Layout.m_TitleHeight + m_Layout.m_BorderSize,
                       m_Layout.m_YAxisWidth + m_Layout.m_BorderSize + m_Layout.m_ControlRect.Width() - m_Layout.m_YAxisWidth - 2 *
                       m_Layout.m_BorderSize,
                       m_Layout.m_ControlRect.Height() - XAxisHeight - m_Layout.m_BorderSize);
    drawMouseTracker(CursorPosition, XMin, XMax, YMin, YMax, Rect);
    glLoadIdentity();
    gluOrtho2D(XMin, XMax, YMin, YMax);

    for(auto GraphIterator = GraphData.begin(); GraphIterator != GraphData.end(); GraphIterator++)
    {
        if(GraphIterator->second.m_Visible && GraphIterator->second.m_Points.size() > 0)
        {
            if(GraphIterator->second.isSelectionActive())
            {
                // Split in 3 parts
                glEnableClientState(GL_VERTEX_ARRAY);

                if(GraphIterator->second.m_DrawLine == true)
                {
                    COLORREF SelectionColor = 0xFFFFFF - GraphIterator->second.m_LineColor;

                    if(SelectionColor == 0xFFFFFF)
                    {
                        SelectionColor = RGB(255, 0, 0);
                    }

                    glColor3i(GET_GLINT_R(GraphIterator->second.m_LineColor), GET_GLINT_G(GraphIterator->second.m_LineColor),
                              GET_GLINT_B(GraphIterator->second.m_LineColor));
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[0]);
                    glDrawArrays(GL_LINE_STRIP, 0, GraphIterator->second.m_SelectionMinIndex + 1);
                    glColor3i(GET_GLINT_R(SelectionColor), GET_GLINT_G(SelectionColor), GET_GLINT_B(SelectionColor));
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[GraphIterator->second.m_SelectionMinIndex]);
                    glDrawArrays(GL_LINE_STRIP, 0, GraphIterator->second.m_SelectionMaxIndex - GraphIterator->second.m_SelectionMinIndex +
                                 1);
                    glColor3i(GET_GLINT_R(GraphIterator->second.m_LineColor), GET_GLINT_G(GraphIterator->second.m_LineColor),
                              GET_GLINT_B(GraphIterator->second.m_LineColor));
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[GraphIterator->second.m_SelectionMaxIndex]);
                    glDrawArrays(GL_LINE_STRIP, 0, GraphIterator->second.m_Points.size() - GraphIterator->second.m_SelectionMaxIndex);

                    if(GraphIterator->second.m_SelectionMaxIndex != GraphIterator->second.m_Points.size() - 1)
                    {
                        glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[0]);
                        glDrawArrays(GL_POINTS, 0, GraphIterator->second.m_Points.size());
                    }
                }

                if(GraphIterator->second.m_PointSize > 0)
                {
                    COLORREF SelectionColor = 0xFFFFFF - GraphIterator->second.m_PointColor;

                    if(SelectionColor == 0xFFFFFF)
                    {
                        SelectionColor = RGB(255, 0, 0);
                    }

                    glColor3i(GET_GLINT_R(GraphIterator->second.m_PointColor), GET_GLINT_G(GraphIterator->second.m_PointColor),
                              GET_GLINT_B(GraphIterator->second.m_PointColor));
                    glPointSize((GLfloat) GraphIterator->second.m_PointSize);
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[0]);
                    glDrawArrays(GL_POINTS, 0, GraphIterator->second.m_SelectionMinIndex);
                    glColor3i(GET_GLINT_R(SelectionColor), GET_GLINT_G(SelectionColor), GET_GLINT_B(SelectionColor));
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[GraphIterator->second.m_SelectionMinIndex]);
                    glDrawArrays(GL_POINTS, 0, GraphIterator->second.m_SelectionMaxIndex - GraphIterator->second.m_SelectionMinIndex + 1);

                    if(GraphIterator->second.m_SelectionMaxIndex != GraphIterator->second.m_Points.size() - 1)
                    {
                        glColor3i(GET_GLINT_R(GraphIterator->second.m_PointColor), GET_GLINT_G(GraphIterator->second.m_PointColor),
                                  GET_GLINT_B(GraphIterator->second.m_PointColor));
                        glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[GraphIterator->second.m_SelectionMaxIndex + 1]);
                        glDrawArrays(GL_POINTS, 0, GraphIterator->second.m_Points.size() - GraphIterator->second.m_SelectionMaxIndex);
                    }
                }

                glDisableClientState(GL_VERTEX_ARRAY);
            }
            else
            {
                // Draw as a whole
                glEnableClientState(GL_VERTEX_ARRAY);

                if(GraphIterator->second.m_DrawLine == true)
                {
                    glColor3i(GET_GLINT_R(GraphIterator->second.m_LineColor), GET_GLINT_G(GraphIterator->second.m_LineColor),
                              GET_GLINT_B(GraphIterator->second.m_LineColor));
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[0]);
                    glDrawArrays(GL_LINE_STRIP, 0, GraphIterator->second.m_Points.size());
                }

                if(GraphIterator->second.m_PointSize > 0)
                {
                    glColor3i(GET_GLINT_R(GraphIterator->second.m_PointColor), GET_GLINT_G(GraphIterator->second.m_PointColor),
                              GET_GLINT_B(GraphIterator->second.m_PointColor));
                    glPointSize((GLfloat) GraphIterator->second.m_PointSize);
                    glVertexPointer(2, GL_DOUBLE, 0, &GraphIterator->second.m_Points[0]);
                    glDrawArrays(GL_POINTS, 0, GraphIterator->second.m_Points.size());
                }

                glDisableClientState(GL_VERTEX_ARRAY);
            }
        }
    }
}

void _GraphArea::drawBackground(_u32 BorderSize, _u32 YAxisWidth, _u32 XAxisHeight, _u32 TitleHeight)
{
    glViewport(m_Layout.m_YAxisWidth + m_Layout.m_BorderSize,
               m_Layout.m_XAxisHeight + m_Layout.m_BorderSize,
               m_Layout.m_ControlRect.Width() - m_Layout.m_YAxisWidth - 2 * m_Layout.m_BorderSize,
               m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - m_Layout.m_TitleHeight - 2 * m_Layout.m_BorderSize);
    glLoadIdentity();
    gluOrtho2D(0, 1, 0, 1);
    glRectd(0, 1, 0, 1);
}

void _GraphArea::setSelectionActive(bool Enable)
{
    m_SelectionActive = Enable;
}

void _GraphArea::drawZoomRect(CPoint Start, CPoint End)
{
    // Zoom rect
    glPushMatrix();
    glColor3d(0, 0, 0);
    glLoadIdentity();
    gluOrtho2D(0, m_Layout.m_ControlRect.Width(), 0, m_Layout.m_ControlRect.Height());
    glViewport(0, 0, m_Layout.m_ControlRect.Width(), m_Layout.m_ControlRect.Height());
    glEnable(GL_POLYGON_STIPPLE);
    glPolygonStipple(ZoomStipple);
    glRectf((_f32)Start.x, (_f32)(m_Layout.m_ControlRect.Height() - Start.y), (_f32)End.x,
            (_f32)(m_Layout.m_ControlRect.Height() - Start.y - (End.y - Start.y)));
    glDisable(GL_POLYGON_STIPPLE);
    glPopMatrix();
}

void _GraphArea::enableMouseTracker(bool Enable)
{
    m_MouseTrackingEnabled = Enable;
}

void _GraphArea::drawMouseTracker(CPoint CursorPosition, double MinX, double MaxX, double MinY, double MaxY,
                                  CRect GraphAreaRect)
{
    if(m_MouseTrackingEnabled)
    {
        double XRange =  MaxX - MinX;
        double YRange =  MaxY - MinY;
        // Mouse tracking
        glColor3d(0, 0, 0);
        CString TrackingString;
        double XPixelOffset = CursorPosition.x - GraphAreaRect.left;
        double X = MinX + XRange * (XPixelOffset / GraphAreaRect.Width());
        double YPixelOffset = CursorPosition.y - GraphAreaRect.top;
        double Y = MaxY - YRange * (YPixelOffset / GraphAreaRect.Height());
        TrackingString.Format(_T("x: %E, y: %E"), X, Y);
        glLoadIdentity();
        gluOrtho2D(0, GraphAreaRect.Width(), 0, GraphAreaRect.Height());
        drawText(TrackingString,
                 OGL_DRAWABLE_FONT_ARIAL_10,
                 CursorPosition.x - m_Layout.m_YAxisWidth - m_Layout.m_BorderSize + 15,  // X-Position
                 GraphAreaRect.Height() + m_Layout.m_TitleHeight - CursorPosition.y - 25 // Y-Position
                 , 0, 0, 0);                                                             // Color
    }
}

double _GraphArea::getXValue(_u32 XPosition)
{
    // Position relative to complete control window
    _s32   PositionOnGraph = XPosition - m_Layout.m_YAxisWidth - m_Layout.m_BorderSize;
    _s32   GraphAreaWidth  = m_Layout.m_ControlRect.Width() - m_Layout.m_YAxisWidth - 2 * m_Layout.m_BorderSize;
    double RangePerPixel   = m_XRange / GraphAreaWidth;
    return (m_XMin + PositionOnGraph * RangePerPixel);
}

double _GraphArea::getYValue(_u32 YPosition)
{
    // Position relative to complete control window
    _u32   PositionOnGraph = YPosition - m_Layout.m_TitleHeight - m_Layout.m_BorderSize;
    _u32   GraphAreaHeight = m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - m_Layout.m_TitleHeight - 2 *
                             m_Layout.m_BorderSize;
    double RangePerPixel   = m_YRange / GraphAreaHeight;
    return (m_YMax - PositionOnGraph * RangePerPixel);
}

_u32 _GraphArea::getWidth()
{
    return m_Layout.m_ControlRect.Width() - m_Layout.m_YAxisWidth - 2 * m_Layout.m_BorderSize;
}

_u32 _GraphArea::getHeight()
{
    return m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - m_Layout.m_TitleHeight - 2 * m_Layout.m_BorderSize;
}

_s32 _GraphArea::getXPosition(double XValue)
{
    if(XValue < m_XMin || XValue > m_XMax)
    {
        return -1;
    }
    else
    {
        _u32   GraphAreaWidth  = m_Layout.m_ControlRect.Width() - m_Layout.m_YAxisWidth - 2 * m_Layout.m_BorderSize;
        double RangePerPixel = m_XRange / GraphAreaWidth;
        double Offset = XValue - m_XMin;
        return (_s32)(m_Layout.m_BorderSize + m_Layout.m_YAxisWidth + Offset / RangePerPixel);
    }
}

_s32 _GraphArea::getYPosition(double YValue)
{
    if(YValue < m_YMin || YValue > m_YMax)
    {
        return -1;
    }
    else
    {
        _u32   GraphAreaHeight = m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - m_Layout.m_TitleHeight - 2 *
                                 m_Layout.m_BorderSize;
        double RangePerPixel = m_YRange / GraphAreaHeight;
        double Offset = YValue - m_YMin;
        return (_s32)(m_Layout.m_ControlRect.Height() - m_Layout.m_BorderSize - m_Layout.m_XAxisHeight - Offset / RangePerPixel);
    }
}

bool _GraphArea::getMouseTrackerEnabled()
{
    return m_MouseTrackingEnabled;
}